#ifndef	__vector_array_H__
#define	__vector_array_H__

#include <stdlib.h>
#include <memory.h>

namespace rend
{

////////////////////////////////////////////////////////////////////////////////////////////////////
// vector array with random access, immutable version
//
// class is not a container and as such exerts no data ownership
////////////////////////////////////////////////////////////////////////////////////////////////////

template < typename BASETYPE_T, unsigned DIMENSION_T >
class VectorArrayImm
{
	const BASETYPE_T (*p)[DIMENSION_T];
	unsigned n;

public:

	VectorArrayImm()
	: p(NULL)
	, n(0)
	{}

	VectorArrayImm(const BASETYPE_T (*first)[DIMENSION_T], const unsigned count);

	unsigned count() const;

	const BASETYPE_T (& get(const unsigned i) const)[DIMENSION_T];
};


template < typename BASETYPE_T, unsigned DIMENSION_T >
inline VectorArrayImm< BASETYPE_T, DIMENSION_T >::VectorArrayImm(
	const BASETYPE_T (*first)[DIMENSION_T],
	const unsigned count)
: p(first)
, n(count)
{
	assert((first && count) || (!first && !count));
}


template < typename BASETYPE_T, unsigned DIMENSION_T >
inline unsigned VectorArrayImm< BASETYPE_T, DIMENSION_T >::count() const
{
	return n;
}


template < typename BASETYPE_T, unsigned DIMENSION_T >
inline const BASETYPE_T (& VectorArrayImm< BASETYPE_T, DIMENSION_T >::get(const unsigned i) const)[DIMENSION_T]
{
	assert(p && i < n);

	return p[i];
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// vector array with random access, mutable version
//
// class is a container and exerts exclusive data ownership
////////////////////////////////////////////////////////////////////////////////////////////////////

template < typename BASETYPE_T, unsigned DIMENSION_T >
class VectorArray
{
	BASETYPE_T (*p)[DIMENSION_T];
	unsigned n;

public:

	VectorArray()
	: p(NULL)
	, n(0)
	{}

	VectorArray(unsigned count)
	: p(reinterpret_cast< BASETYPE_T(*)[DIMENSION_T] >(::malloc(count * sizeof(BASETYPE_T[DIMENSION_T]))))
	, n(count)
	{}

	VectorArray(const VectorArray &src);

	~VectorArray();

	// resize the array, discarding its original content
	void realloc(const unsigned count);

	// obtain ownership over peer's content
	void transfer(VectorArray &src);

	// pass content ownership onto a non-peer
	void drain(BASETYPE_T (*&first)[DIMENSION_T], unsigned& count);

	// absorb content from a non-peer; caller responsible for voiding original ownership
	void absorb(BASETYPE_T (*first)[DIMENSION_T], const unsigned count);

	// copy content from source
	VectorArray& operator =(const VectorArray &src);

	// cast to immutable self
	operator const VectorArrayImm< BASETYPE_T, DIMENSION_T >& () const;

	// get vector count
	unsigned count() const;

	// get individual vector
	BASETYPE_T (& get(const unsigned i) const)[DIMENSION_T];
};


template < typename BASETYPE_T, unsigned DIMENSION_T >
inline void VectorArray< BASETYPE_T, DIMENSION_T >::realloc(const unsigned count)
{
	p = reinterpret_cast< BASETYPE_T(*)[DIMENSION_T] >(::realloc(p, count * sizeof(BASETYPE_T[DIMENSION_T])));
	n = count;
}


template < typename BASETYPE_T, unsigned DIMENSION_T >
inline void VectorArray< BASETYPE_T, DIMENSION_T >::transfer(VectorArray &src)
{
	::free(p);

	p = src.p;
	n = src.n;

	src.p = NULL;
	src.n = 0;
}


template < typename BASETYPE_T, unsigned DIMENSION_T >
inline void VectorArray< BASETYPE_T, DIMENSION_T >::drain(BASETYPE_T (*&first)[DIMENSION_T], unsigned& count)
{
	first = p;
	count = n;

	p = NULL;
	n = 0;
}


template < typename BASETYPE_T, unsigned DIMENSION_T >
inline void VectorArray< BASETYPE_T, DIMENSION_T >::absorb(BASETYPE_T (*first)[DIMENSION_T], const unsigned count)
{
	::free(p);

	p = first;
	n = count;
}


template < typename BASETYPE_T, unsigned DIMENSION_T >
inline VectorArray< BASETYPE_T, DIMENSION_T >& VectorArray< BASETYPE_T, DIMENSION_T >::operator =(const VectorArray &src)
{
	realloc(src.n);

	::memcpy(p, src.p, src.n * sizeof(BASETYPE_T[DIMENSION_T]));

	return *this;
}


template < typename BASETYPE_T, unsigned DIMENSION_T >
inline VectorArray< BASETYPE_T, DIMENSION_T >::VectorArray(const VectorArray &src)
: p(NULL)
{
	*this = src;
}


template < typename BASETYPE_T, unsigned DIMENSION_T >
inline VectorArray< BASETYPE_T, DIMENSION_T >::~VectorArray()
{
	assert((p == NULL && n == 0) || (p != NULL && n != 0));

	::free(p);
}


template < typename BASETYPE_T, unsigned DIMENSION_T >
inline VectorArray< BASETYPE_T, DIMENSION_T >::operator const VectorArrayImm< BASETYPE_T, DIMENSION_T >& () const
{
	return *reinterpret_cast< const VectorArrayImm< BASETYPE_T, DIMENSION_T >* >(this);
}


template < typename BASETYPE_T, unsigned DIMENSION_T >
inline unsigned VectorArray< BASETYPE_T, DIMENSION_T >::count() const
{
	return n;
}


template < typename BASETYPE_T, unsigned DIMENSION_T >
inline BASETYPE_T (& VectorArray< BASETYPE_T, DIMENSION_T >::get(const unsigned i) const)[DIMENSION_T]
{
	assert(p && i < n);

	return p[i];
}

} // namespace rend

#endif // __vector_array_H__
